package com.minigame.card.mahjong.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static cn.hutool.core.collection.CollUtil.countMap;
import static com.minigame.card.mahjong.util.MahjongUtil.mahjongCommonHu;
import static com.minigame.card.util.CardUtil.countPlayerCell;

@AllArgsConstructor
@Getter
public enum HuRule {
    J("将一色") {
        @Override
        public boolean rule(List<MahjongCard> up, List<List<MahjongCard>> down, boolean gang) {
            return jiangFengRule(up, down, MahjongCard::isJiang);
        }
    },
    F("风一色") {
        @Override
        public boolean rule(List<MahjongCard> up, List<List<MahjongCard>> down, boolean gang) {
            return jiangFengRule(up, down, MahjongCard::isFeng);
        }
    },
    Q("清一色") {
        @Override
        public boolean rule(List<MahjongCard> up, List<List<MahjongCard>> down, boolean gang) {
            char type = up.get(0).getType();
            if (down != null) {
                if (!down.stream().allMatch(team -> team.stream().allMatch(card -> type == card.getType()))) {
                    return false;
                }
            }
            if (!up.stream().allMatch(c -> type == c.getType())) {
                return false;
            }
            int[] upCounts = countPlayerCell(up, MahjongCard.values().length, MahjongCard::getIndex);
            return mahjongCommonHu(upCounts, false);
        }
    },
    D("七对") {
        @Override
        public boolean rule(List<MahjongCard> up, List<List<MahjongCard>> down, boolean gang) {
            if (down != null && !down.isEmpty()) {
                return false;
            }
            return countMap(up).values().stream().allMatch(count -> count == 2);
        }
    },
    P("碰碰胡") {
        @Override
        public boolean rule(List<MahjongCard> up, List<List<MahjongCard>> down, boolean gang) {
            if (!down.stream().allMatch(team -> countMap(team).values().stream().allMatch(count -> count >= 3))) {
                return false;
            }
            int jiang = 0;
            for (Integer count : countMap(up).values()) {
                if (count == 2) {
                    jiang++;
                } else if (count != 3) {
                    return false;
                }
            }
            return jiang == 1;
        }
    },
    G("杠上开花") {
        @Override
        public boolean rule(List<MahjongCard> up, List<List<MahjongCard>> down, boolean gang) {
            int[] upCounts = countPlayerCell(up, MahjongCard.values().length, MahjongCard::getIndex);
            boolean requiredJiang = Stream.of(J, F, Q, D, P).allMatch(hu -> hu.rule(up, down, gang));
            return gang && mahjongCommonHu(upCounts, requiredJiang);
        }
    },
    Z("屁胡") {
        @Override
        public boolean rule(List<MahjongCard> up, List<List<MahjongCard>> down, boolean gang) {
            int[] upCounts = countPlayerCell(up, MahjongCard.values().length, MahjongCard::getIndex);
            return mahjongCommonHu(upCounts, true);
        }
    },
    ;
    private final String name;

    /**
     * 胡牌规则
     * @param up 手牌
     * @param down 落地牌
     * @return 能否胡
     */
    public abstract boolean rule(List<MahjongCard> up, List<List<MahjongCard>> down, boolean gang);

    /**
     * 将一色和风一色混牌即可胡
     * @param up 手牌
     * @param down 落地牌
     * @param matcher 匹配规则
     * @return 能否胡
     */
    private static boolean jiangFengRule(List<MahjongCard> up, List<List<MahjongCard>> down, Predicate<MahjongCard> matcher) {
        if (down != null) {
            for (List<MahjongCard> team : down) {
                if (!team.stream().allMatch(matcher)) {
                    return false;
                }
            }
        }
        return up.stream().allMatch(matcher);
    }
}
